package bmaso.purest;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class UriUtils {
	private static Pattern uriRegexPattern =
		Pattern.compile("^((?:/(?:(?:;\\?)|(?:\\{[^\\}/]*)|[^/\\?#])*)+)(?:\\?([^#]*))?(?:#(.*))?$");
	private static Pattern pathSegmentRegexPattern =
		Pattern.compile("(?:/([^/]*))");
	private static Pattern queryParameterSegmentRegexPattern =
		Pattern.compile("(?:([^=]+)=([^&]+)\\&?)");
	
	private static final Pattern segmentMatrixMembersRegexPattern =
		Pattern.compile("(;[^=]+=[^;]+)+$");
	private static final Pattern segmentMatrixMemberRegexPattern =
		Pattern.compile(";([^=]+)=([^;]+)");
	
	public static List<String> uriPathSegments(String uri) {
		List<String> ret = new LinkedList<String>();
		
		Matcher uriMatcher = uriRegexPattern.matcher(uri);
		if(!uriMatcher.matches()) {
			return Collections.emptyList();
		}
		
		String path = uriMatcher.group(1);
		Matcher pathSegmentMatcher = pathSegmentRegexPattern.matcher(path);
		while(pathSegmentMatcher.find()) {
			String v = pathSegmentMatcher.group(1);
			ret.add(v);
		}
		
		return ret;
	}
	
	public static Map<String, String> queryParameterNamesAndValues(String queryString) {
		Map<String, String> ret = new HashMap<String, String>();
		
		Matcher uriMatcher = uriRegexPattern.matcher("/dummy?" + queryString);
		if(!uriMatcher.matches()) {
			return Collections.emptyMap();
		}
		
		String q = uriMatcher.group(2);
		Matcher queryParameterSegmentMatcher = queryParameterSegmentRegexPattern.matcher(q);
		while(queryParameterSegmentMatcher.find()) {
			String name = urlunencode(queryParameterSegmentMatcher.group(1));
			String value = urlunencode(queryParameterSegmentMatcher.group(2));
			ret.put(name, value);
		}
		
		return ret;
	}
	
	public static Map<String, String> segmentMatrixPatameterNamesAndValues(String segment) {
		Matcher segmentMatcher = segmentMatrixMembersRegexPattern.matcher(segment);
		if(segmentMatcher.find()) {
			String matrixParamsPortion = segmentMatcher.group();
			Map<String, String> ret = new HashMap<String, String>();
			Matcher matrixMatcher = segmentMatrixMemberRegexPattern.matcher(matrixParamsPortion);
			while(matrixMatcher.find()) {
				String name = matrixMatcher.group(1);
				String value = matrixMatcher.group(2);
				ret.put(urlunencode(name), urlunencode(value));
			}
			return ret;
		} else {
			return Collections.emptyMap();
		}
	}
	
	public static String segmentPathWithoutMatrixParameters(String segment) {
		Matcher segmentMatcher = segmentMatrixMembersRegexPattern.matcher(segment);
		if(segmentMatcher.find()) {
			return segment.substring(0, segmentMatcher.start());
		} else {
			//...segment does not have matrix parameters (that is, not all
			//   are well-formed, or there are none at all...
			return segment;
		}
	}
	
	public static String urlunencode(String in) {
		try {
			return URLDecoder.decode(in, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			//...should never happen...
			throw new RuntimeException(e);
		}
	}
}
